// Copyright (C) 2010 Félix Cloutier.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#ifndef LLVM_JIT_CONTEXT
#define LLVM_JIT_CONTEXT

#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/Function.h>
#include <llvm/BasicBlock.h>
#include <llvm/DerivedTypes.h>
#include <llvm/Support/IRBuilder.h>
#include <llvm/Value.h>

#include <string>
#include <map>
#include <set>
#include <stack>

#include "Common.h"

namespace LLVMJit
{

// The JitFunctionContext class represents all the available information on the
//	LLVM function currently being recompiled. This includes the arguments and
//	references to the BasicBlocks composing it, indexed by address. It also
//	helps keep track of the number of cycles elapsed at a certain point in the
//	generated code.

// It holds the IRBuilder for the function. The object can be accessed through
//	GetBuilder().

// A JitFunctionContext object is passed by reference to every code-generating
//	function in the JitRecompiler class.
class JitFunctionContext
{	
	llvm::LLVMContext& context;
	llvm::IRBuilder<> builder;
	llvm::Function* function;
	llvm::Value* arguments[3];
	llvm::Value* cycleCounter;
	llvm::BasicBlock* entryBlock;
	std::map<u32, llvm::BasicBlock*> labelMap;
	u32 pc;
	
	std::set<u32> allBranches;
	std::stack<u32> branchesLeft;
	
public:
	JitFunctionContext(llvm::Module* module, const std::string& functionName);
	
	llvm::IRBuilder<>& GetBuilder();
	
	llvm::BasicBlock* GetEntryBlock();
	llvm::BasicBlock* GetBlock(u32 address);
	llvm::BasicBlock* MakeConditionalBlock(bool successOfCondition);
	
	llvm::Value* GetJitExecutor();
	llvm::Value* GetCoroutine();
	llvm::Value* GetPowerPCState();
	
	void PushBranch(u32 branch);
	u32 PopBranch();
	bool HasMoreBranches();
	
	u32 GetPC();
	void SetPC(u32 pc);
	
	llvm::Value* GetCycles();
	void AddCycles(u32 cycles);
	void AddCycles(llvm::Value* cyclesIncrement);
	
	llvm::Function* GetFunction();
};

}

#endif